#install.packages("knitr")
#install.packages("grid")
install.packages("plotly")
Error in install.packages : Updating loaded packages
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")

#install.packages("RColorBrewer")
#install.packages("rgdal")
#install.packages("jsonlite")
#install.packages("readr")


library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)

library(leaflet)
#library(plotly)
library(RColorBrewer)
package 㤼㸱RColorBrewer㤼㸲 was built under R version 4.0.3
library(readr)
package 㤼㸱readr㤼㸲 was built under R version 4.0.3
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
  dbname = "coronavirus",
  host = "localhost",
  user = "root",
  password = "1234"
)
`src_mysql()` is deprecated as of dplyr 1.0.0.
Please use `tbl()` directly with a database connection
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
my_db
src:  mysql 8.0.21 [root@localhost:/coronavirus]
tbls: avg_world_temp_2020, covid_thailand, covid_us, covid19_confirmed, covid19_deaths,
  covid19_recovered, covidus, gdp, gdp19, healthranking, population, pornhub, sars,
  sars_2003, sars_2003_update, top20pornhub
##import data
df_conf <- tbl(my_db, sql("select * from covid19_confirmed "))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid19_deaths "))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid19_recovered "))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-14"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
  ## remove some columns
  data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
  ## convert from wide to long format
  data %<>% gather(key=date, value=count, -country)
  ## convert from character to date
  data %<>% mutate(date = date %>% mdy())
  ## aggregate by country
  data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
  return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data 
data.world <- data %>% group_by(date) %>%
  summarise(country='World',
            confirmed = sum(confirmed, na.rm=T),
            deaths = sum(deaths, na.rm=T),
            recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
data
NA
#rate
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
                 new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
                 new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
                 new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1))
View(data)
## convert from wide to long format
data.long <- data %>%
  select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
  gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
                                         current.confirmed='Current Confirmed',
                                         recovered='Recovered',
                                         deaths='Deaths'))
#View(data.long)
##Number of case World
world <- filter(data.long,country == 'World')
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
  ggplot(aes(x=date, y=count)) +
  geom_area(aes(fill=type), alpha=0.5) +
  labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
  scale_fill_manual(values=c('red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=7),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=6),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
  ggplot(aes(x=date, y=count)) +
  geom_line(aes(color=type)) +
  labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
  scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=14),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=14),
        axis.text=element_text(size=14),
        axis.text.x=element_text(angle=45, hjust=1)) +
  scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

plot2

## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

View(data.world)
## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
rates.long <- data %>%
  select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
  gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
                             rate.upper='Upper bound'))
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
  select(country, date,confirmed, new.confirmed, current.confirmed,
         recovered, deaths, new.deaths, death.rate=rate.lower) %>%
  mutate(ranking = dense_rank(desc(confirmed)))
#View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
  arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
 [1] "US"             "India"          "Brazil"         "Russia"         "United Kingdom"
 [6] "France"         "Turkey"         "Italy"          "Spain"          "Germany"       
[11] "Colombia"       "Argentina"      "Mexico"         "Poland"         "Iran"          
[16] "South Africa"   "Ukraine"        "Peru"           "Netherlands"    "Indonesia"     
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
  mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
  mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
  summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
            current.confirmed=sum(current.confirmed),
            recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
  mutate(death.rate=(100 * deaths/confirmed) %>% round(1)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths, current.confirmed,recovered)) %>%
  mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
df_pop
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE) 
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest
data.latest  %<>% select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths,
                          current.confirmed,recovered,recover.rate,population)) %>%
  mutate(confirm.rate=(100 *confirmed/population) %>% round(1))
data.latest
NA
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
NA
NA
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
  gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
                                                confirmed='Total Confirmed',
                                                deaths='Total Deaths',
                                                death.rate='Death Rate (%)',
                                                new.confirmed='New Confirmed (compared with one day before)',
                                                new.deaths='New Deaths (compared with one day before)',
                                                current.confirmed='Current Confirmed',
                                                recover.rate = 'Recover Rate(%)',
                                                confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
                       | type=='Total Deaths'
                       | type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
                       | type=='New Confirmed (compared with one day before)'
                       | type=='New Deaths (compared with one day before)'
                       | type=='Recover Rate(%)'
                       | type=='Confirmed Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=2, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=11),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~type, ncol=1, scales='free_y')


data.two.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=2, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=11),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~type, ncol=1, scales='free_y')

##GDP
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
df_healt
#Top20Pornhub
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
NA
#temp
df_temp <- tbl(my_db, sql("select * from Avg_World_Temp_2020"))
df_temp <- as.data.frame(df_temp)
df_city <- select(df_temp,c("Country","City")) %>%
  rename(country=Country) %>% 
  rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Apr","May","Jun","Jul","Aug")) %>%
  rename(country=Country)
df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(avg,na.rm = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp <- df_temp %>% mutate(country=ifelse(country=="United States","US", country ) ) 
df_temp$avg_temp <- df_temp$avg_temp %>% 
  sprintf(df_temp$avg_temp, fmt = '%#.1f') %>%
  as.numeric(df_temp$avg_temp)
df_temp
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World')
data.top <- head(data.top,20)
#View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
#View(data.gdp)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "country", all.x = TRUE) 
  return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>% 
  select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>% 
  rename(GDP="GDP (millions of US dollars)")

data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
  rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)

data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
  rename(Pornhub = "PornhubIndex(%)")

data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
#View(data.top.world)

normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:12],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
#View(norm_data)


norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type, 
                      level = c("GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate","Pornhub"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) + 
  geom_tile() +
  scale_fill_gradient(low = "pink", high = "blue") +
  xlab("") +
  ylab("") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90,vjust = 1))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  )

NA
NA
#rank GDP
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
  mutate(ranking = dense_rank(desc(GDP)))
data.top.hight
k <- 15
top.gdp <- data.top.hight %>% 
  #filter(ranking <= k + 1) %>% 
  arrange(ranking)
top.gdp <- head(top.gdp,21)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
  mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>% 
  #filter(ranking <= k + 1) %>% 
  arrange(ranking)
View(low.gdp.long)
low.gdp <- head(low.gdp.long,23)
low.gdp
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare,avg_temp,Pornhub))
head(corr_data)
cor(corr_data)
                    GDP confirm.rate  death.rate recover.rate  healthcare    avg_temp
GDP           1.0000000    0.4350394 -0.23717777   -0.4763494  0.25183938  0.15986445
confirm.rate  0.4350394    1.0000000 -0.34002060   -0.7254358  0.46302123 -0.38053574
death.rate   -0.2371778   -0.3400206  1.00000000    0.1832321 -0.16021245  0.31063014
recover.rate -0.4763494   -0.7254358  0.18323215    1.0000000 -0.73011486  0.12398936
healthcare    0.2518394    0.4630212 -0.16021245   -0.7301149  1.00000000 -0.05087304
avg_temp      0.1598645   -0.3805357  0.31063014    0.1239894 -0.05087304  1.00000000
Pornhub       0.6997223    0.4557707 -0.07822766   -0.4581646  0.35166155  0.10674647
                 Pornhub
GDP           0.69972226
confirm.rate  0.45577066
death.rate   -0.07822766
recover.rate -0.45816457
healthcare    0.35166155
avg_temp      0.10674647
Pornhub       1.00000000
ggcorrplot(cor(corr_data),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

df <- data.long %>% filter(country %in% top.countries) %<>%
  mutate(country=country %>% factor(levels=c(top.countries)))
df %>% filter(country != 'World' & type != 'Total Confirmed') %>%
  ggplot(aes(x=date, y=count, fill=type)) +
  geom_area(alpha=0.5) +
# xlab('') + ylab('') +
  labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries - ',
                    max.date.txt)) +
  scale_fill_manual(values=c('red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=12),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.4, 'cm'),
        legend.text=element_text(size=12),
        strip.text.x=element_text(size=12),
        axis.text=element_text(size=12),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~country, ncol=4, scales='free_y')

p <- df %>% filter(country != 'World') %>%
  ggplot(aes(x=date, y=count, color=type)) +
  geom_line() +
  labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries (log scale) - ',
                    max.date.txt)) +
  scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=10),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.4, 'cm'),
        legend.text=element_text(size=10),
        strip.text.x=element_text(size=10),
        axis.text=element_text(size=10),
        axis.text.x=element_text(angle=45, hjust=1)) +
  scale_y_continuous(trans='log10')
p + facet_wrap(~country, ncol=4, scales='free_y')

data.world %<>% arrange(desc(date)) %>%
  select(c(date, confirmed, deaths, recovered, current.confirmed,new.confirmed, new.deaths, new.recovered, rate.lower, rate.upper, rate.daily))
data.world %>%
  mutate(rate.upper = rate.upper %>% format(nsmall=1) %>% paste0('\\%'),
         rate.lower = rate.lower %>% format(nsmall=1) %>% paste0('\\%'),
         rate.daily = rate.daily %>% format(nsmall=1) %>% paste0('\\%')) 
#sars_2003
df_sars <- tbl(my_db, sql("select * from sars_2003_update"))
df_sars <- as.data.frame(df_sars)
df_sars
#datesSar <- as.Date(df_sars$Date,format = "%m/%d/%y")
#datesSar

df_sars %<>%  mutate(Date = as.Date(df_sars$Date,format = "%m/%d/%y"))
df_sars
## convert from wide to long format
dataSar.long <- df_sars %>%
  select(c(Date, country, Cumulative_number , Number_deaths, Number_recovered)) %>%
  gather(key=type, value=count, -c(country, Date))
## set factor levels to show them in a desirable order
dataSar.long %<>% mutate(type=recode_factor(type, Cumulative_number ='Cumulative Number',
                                         Number_deaths ='Number of deaths',
                                         Number_recovered ='Number of recovered'))
View(dataSar.long)
 
g <-
  ggplot(dataSar.long,aes(Date,count,color = type)) +
  geom_line()+
  geom_point()+
  xlab("")+
  ylab("")
g

NA
#Covid_Thailand
df_thai <- tbl(my_db, sql("select * from covid_Thailand"))
df_thai <- as.data.frame(df_thai)
df_thai
#clean Covid_Thailand
dates.th <- df_thai[,2]%>% mdy()
range(dates.th)
[1] "2020-01-12" "2021-01-15"
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai 
df_thai <- df_thai %>% select(!No.) %>% select(!notification_date) %>% 
  group_by(announce_date)
df_thai
# Total confirmed cases in Thailand
data.thai.count <- df_thai %>%
  select(announce_date) %>%
  summarise(comfirmed = n())  %>% as.data.frame()
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
data.thai.count
## Thai Confirmed Cases (Jan 2020 - Jan 2021
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)

## Thai Confirmed Cases (Jan 2020 - Jan 2021) log scale
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)

# Confirmed cases divided by sex (gender)
data.thai.gender <- df_thai %>%
  group_by(sex) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>1)%>%
  #mutate(pos = cumsum(percent) - 0.5*percent) %>%
  arrange(desc(percent))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.gender
NA
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
data.thai.gender$sex
[1] Male   Female
Levels: Male Female
g.th.gender <- data.thai.gender %>% 
  ggplot(aes(x = "", y = percent, fill = sex)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y") +
  theme_void() +
  labs(title='Gender of Thai Confirmed Cases (Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.gender

# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>3.5) %>%
  arrange(desc(percent)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
data.thai.risk$risk[data.thai.risk$risk == "C"] <- "Close contact with the patient"
data.thai.risk$risk[data.thai.risk$risk == "F"] <- "State Quarantine"
data.thai.risk$risk[data.thai.risk$risk == "O"] <- "AOQ/ALQ/HQ/AHQ/OQ"
data.thai.risk$risk[data.thai.risk$risk == "G"] <- "Go to a crowded place"
data.thai.risk$risk[data.thai.risk$risk == "B"] <- "Thai people from abroad"
data.thai.risk$risk[data.thai.risk$risk == "D"] <- "Career at risk"
data.thai.risk$risk[data.thai.risk$risk == "H"] <- "Cabaret"
data.thai.risk
data.thai.risk$risk <- factor(data.thai.risk$risk, levels = as.character(data.thai.risk$risk))
data.thai.risk$risk
 [1] Close contact with the patient Cluster Samut Sakhon          
 [3] State Quarantine               Unknown                       
 [5] AOQ/ALQ/HQ/AHQ/OQ              Go to a crowded place         
 [7] Thai people from abroad        Cluster Rayong                
 [9] Career at risk                 Cabaret                       
10 Levels: Close contact with the patient Cluster Samut Sakhon State Quarantine ... Cabaret
g.th.risk <- data.thai.risk %>% 
  ggplot(aes(x = "", y = percent, fill = risk)) +
  geom_bar(stat = "identity", width = 0.5) +
  coord_polar("y") +
  theme_void() +
  labs(title='Risk of Thai Confirmed Cases(Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "Black", size = 3, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.risk

# Confirmed cases divided by age
data.thai.age <- df_thai %>%
  group_by(age,sex) %>% 
  filter(sex != "")%>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` regrouping output by 'age' (override with `.groups` argument)
data.thai.age
ggplot(data.thai.age,aes(x=age,y=count,fill=sex))+geom_bar(stat = "identity")+
  labs(title='Age of Thai Confirmed Cases')+guides(fill=guide_legend(reverse = T))

# Confirmed cases divided by nationality
data.thai.nationality <- df_thai %>%
  group_by(nationality) %>%
  summarise(count = n()) %>%
  filter(count > 13)%>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
#data.thai.nationality$nationality[nationality$nationality == "????????"] <- "Unknown"
data.thai.nationality
ggplot(data.thai.nationality,aes(x=nationality,y=count))+geom_bar(stat = "identity")+
  labs(title='Nationality of Thai Confirmed Cases')+coord_flip()

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiNpbnN0YWxsLnBhY2thZ2VzKCJkcHJlcCIpDQojaW5zdGFsbC5wYWNrYWdlcygibm9ybWFsciIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2djb3JycGxvdCIpDQoNCiNpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KI2luc3RhbGwucGFja2FnZXMoInJnZGFsIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJqc29ubGl0ZSIpDQojaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQ0KYGBgDQoNCmBgYHtyfQ0KDQoNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShub3JtYWxyKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KDQpsaWJyYXJ5KGxlYWZsZXQpDQojbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkocmVhZHIpDQojbGlicmFyeShNTFJNUEEpDQojPz9zcmNfbXlzcWwNCm15X2RiIDwtIHNyY19teXNxbCgNCiAgZGJuYW1lID0gImNvcm9uYXZpcnVzIiwNCiAgaG9zdCA9ICJsb2NhbGhvc3QiLA0KICB1c2VyID0gInJvb3QiLA0KICBwYXNzd29yZCA9ICIxMjM0Ig0KKQ0KbXlfZGINCg0KIyNpbXBvcnQgZGF0YQ0KZGZfY29uZiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZDE5X2NvbmZpcm1lZCAiKSkNCmRmX2NvbmYgPC0gYXMuZGF0YS5mcmFtZShkZl9jb25mKQ0KZGZfY29uZg0KZGZfZGVhdGhzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfZGVhdGhzICIpKQ0KZGZfZGVhdGhzIDwtIGFzLmRhdGEuZnJhbWUoZGZfZGVhdGhzKQ0KZGZfZGVhdGhzDQpkZl9yZWNvdmVyIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfcmVjb3ZlcmVkICIpKQ0KZGZfcmVjb3ZlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3JlY292ZXIpDQpkZl9yZWNvdmVyDQpgYGANCmBgYHtyfQ0KIyNjaGVjayB0aGUgdGltZSBmcmFtZSBvZiB0aGUgZGF0YQ0Kbi5jb2wgPC0gbmNvbChkZl9jb25mKQ0KZGF0ZXMgPC0gbmFtZXMoZGZfY29uZilbNTpuLmNvbF0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzKQ0KbWluLmRhdGUgPC0gbWluKGRhdGVzKQ0KbWF4LmRhdGUgPC0gbWF4KGRhdGVzKQ0KbWluLmRhdGUudHh0IDwtIG1pbi5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dCA8LSBtYXguZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCmBgYHtyfQ0KI2NsZWFuIGRhdGENCmNsZWFuRGF0YSA8LSBmdW5jdGlvbihkYXRhKSB7DQogICMjIHJlbW92ZSBzb21lIGNvbHVtbnMNCiAgZGF0YSAlPD4lIHNlbGVjdCgtYyhQcm92aW5jZS5TdGF0ZSwgTGF0LCBMb25nKSkgJT4lIHJlbmFtZShjb3VudHJ5PUNvdW50cnkuUmVnaW9uKQ0KICAjIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KICBkYXRhICU8PiUgZ2F0aGVyKGtleT1kYXRlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQogICMjIGNvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gZGF0ZQ0KICBkYXRhICU8PiUgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkNCiAgIyMgYWdncmVnYXRlIGJ5IGNvdW50cnkNCiAgZGF0YSAlPD4lIGdyb3VwX2J5KGNvdW50cnksIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9c3VtKGNvdW50LCBuYS5ybT1UKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KICByZXR1cm4oZGF0YSkNCn0NCiMjIGNsZWFuIHRoZSB0aHJlZSBkYXRhIHNldHMNCmRhdGEuY29uZmlybWVkIDwtIGRmX2NvbmYgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoY29uZmlybWVkPWNvdW50KQ0KZGF0YS5kZWF0aHMgPC0gZGZfZGVhdGhzICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKGRlYXRocz1jb3VudCkNCmRhdGEucmVjb3ZlcmVkIDwtIGRmX3JlY292ZXIgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUocmVjb3ZlcmVkPWNvdW50KQ0KZGF0YSA8LSBkYXRhLmNvbmZpcm1lZCAlPiUgbWVyZ2UoZGF0YS5kZWF0aHMsIGFsbD1UKSAlPiUgbWVyZ2UoZGF0YS5yZWNvdmVyZWQsIGFsbD1UKQ0KZGF0YQ0KIyMgY291bnRyaWVzL3JlZ2lvbnMgd2l0aCBjb25maXJtZWQgY2FzZXMsIGV4Y2wuIGNydWlzZSBzaGlwcw0KY291bnRyaWVzIDwtIGRhdGEgJT4lIHB1bGwoY291bnRyeSkgJT4lIHNldGRpZmYoJ0NydWlzZSBTaGlwJykNCmRhdGEgDQpgYGANCg0KDQpgYGB7cn0NCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudHJ5PSdXb3JsZCcsDQogICAgICAgICAgICBjb25maXJtZWQgPSBzdW0oY29uZmlybWVkLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsIG5hLnJtPVQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkID0gc3VtKHJlY292ZXJlZCwgbmEucm09VCkpDQpkYXRhICU8PiUgcmJpbmQoZGF0YS53b3JsZCkNCmRhdGENCmRhdGEgJTw+JSBtdXRhdGUoY3VycmVudC5jb25maXJtZWQgPSBjb25maXJtZWQgLSBkZWF0aHMgLSByZWNvdmVyZWQpDQpkYXRhDQoNCmBgYA0KYGBge3J9DQojcmF0ZQ0KZGF0YSAlPD4lIGFycmFuZ2UoY291bnRyeSwgZGF0ZSkNCm4gPC0gbnJvdyhkYXRhKQ0KZGF5MSA8LSBtaW4oZGF0YSRkYXRlKQ0KZGF0YSAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGRlYXRocyAtIGxhZyhkZWF0aHMsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcucmVjb3ZlcmVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIHJlY292ZXJlZCAtIGxhZyhyZWNvdmVyZWQsIG49MSkpKQ0KZGF0YSAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKG5ldy5jb25maXJtZWQgPCAwLCAwLCBuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShuZXcuZGVhdGhzIDwgMCwgMCwgbmV3LmRlYXRocyksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UobmV3LnJlY292ZXJlZCA8IDAsIDAsIG5ldy5yZWNvdmVyZWQpKQ0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBkZWF0aHMgYW5kIHJlY292ZXJlZCBjYXNlcw0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLnVwcGVyID0gKDEwMCAqIGRlYXRocyAvIChkZWF0aHMgKyByZWNvdmVyZWQpKSAlPiUgcm91bmQoMSkpDQojIyBsb3dlciBib3VuZDogZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBjb25maXJtZWQgY2FzZXMNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS5sb3dlciA9ICgxMDAgKiBkZWF0aHMgLyBjb25maXJtZWQpICU+JSByb3VuZCgxKSkNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdGhlIG51bWJlciBvZiBkZWF0aC9yZWNvdmVyZWQgb24gZXZlcnkgc2luZ2xlIGRheQ0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLmRhaWx5ID0gKDEwMCAqIG5ldy5kZWF0aHMgLyAobmV3LmRlYXRocyArIG5ldy5yZWNvdmVyZWQpKSAlPiUgcm91bmQoMSkpDQpWaWV3KGRhdGEpDQpgYGANCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCBjb25maXJtZWQsIGN1cnJlbnQuY29uZmlybWVkLCByZWNvdmVyZWQsIGRlYXRocykpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXJlZD0nUmVjb3ZlcmVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdEZWF0aHMnKSkNCiNWaWV3KGRhdGEubG9uZykNCmBgYA0KYGBge3J9DQojI051bWJlciBvZiBjYXNlIFdvcmxkDQp3b3JsZCA8LSBmaWx0ZXIoZGF0YS5sb25nLGNvdW50cnkgPT0gJ1dvcmxkJykNCnBsb3QxIDwtIHdvcmxkICU+JSBmaWx0ZXIodHlwZSAhPSAnVG90YWwgQ29uZmlybWVkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50KSkgKw0KICBnZW9tX2FyZWEoYWVzKGZpbGw9dHlwZSksIGFscGhhPTAuNSkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDYXNlcyBXb3JsZHdpZGUgLSAnLCBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIHdvcmxkICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj10eXBlKSkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDYXNlcyBXb3JsZHdpZGUgKGxvZyBzY2FsZSkgLSAnLCBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygncHVycGxlJywgJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKQ0KIyMgc2hvdyB0d28gcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTIpDQpgYGANCg0KDQpgYGB7cn0NCnBsb3QyDQpgYGANCmBgYHtyfQ0KIyMgQ3VycmVudCBDb25maXJtZWQgQ2FzZXMNCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQpuIDwtIG5yb3coZGF0YS53b3JsZCkNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWN1cnJlbnQuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQ3VycmVudCBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nRGFpbHkgTmV3IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KVmlldyhkYXRhLndvcmxkKQ0KYGBgDQpgYGB7cn0NCiMjIGEgc2NhdHRlciBwbG90IHdpdGggYSBzbW9vdGhlZCBsaW5lIGFuZCB2ZXJ0aWNhbCB4LWF4aXMgbGFiZWxzDQpwbG90MSA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1kZWF0aHMpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgRGVhdGhzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9cmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QzIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5kZWF0aHMpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgRGVhdGhzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDQgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LnJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IGZvdXIgcGxvdHMgdG9nZXRoZXIsIHdpdGggMiBwbG90cyBpbiBlYWNoIHJvdw0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgcGxvdDMsIHBsb3Q0LCBucm93PTIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGZvciBkcmF3aW5nIGFyZWEgcGxvdHMNCnJhdGVzLmxvbmcgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgcmF0ZS51cHBlciwgcmF0ZS5sb3dlciwgcmF0ZS5kYWlseSkpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpyYXRlcy5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCByYXRlLmRhaWx5PSdEYWlseScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGUudXBwZXI9J1VwcGVyIGJvdW5kJykpDQpgYGANCmBgYHtyfQ0KIyMgcmFua2luZyBieSBjb25maXJtZWQgY2FzZXMNCmRhdGEubGF0ZXN0LmFsbCA8LSBkYXRhICU+JSBmaWx0ZXIoZGF0ZSA9PSBtYXgoZGF0ZSkpICU+JQ0KICBzZWxlY3QoY291bnRyeSwgZGF0ZSxjb25maXJtZWQsIG5ldy5jb25maXJtZWQsIGN1cnJlbnQuY29uZmlybWVkLA0KICAgICAgICAgcmVjb3ZlcmVkLCBkZWF0aHMsIG5ldy5kZWF0aHMsIGRlYXRoLnJhdGU9cmF0ZS5sb3dlcikgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKGNvbmZpcm1lZCkpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QuYWxsKQ0KayA8LSAyMA0KIyMgdG9wIDIwIGNvdW50cmllczogMjEgaW5jbC4gJ1dvcmxkJw0KdG9wLmNvdW50cmllcyA8LSBkYXRhLmxhdGVzdC5hbGwgJT4lIGZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgYXMuY2hhcmFjdGVyKCkNCnRvcC5jb3VudHJpZXMgJT4lIHNldGRpZmYoJ1dvcmxkJykgJT4lIHByaW50KCkNCg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS5sYXRlc3QgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIoIWlzLm5hKGNvdW50cnkpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9aWZlbHNlKHJhbmtpbmcgPD0gayArIDEsIGFzLmNoYXJhY3Rlcihjb3VudHJ5KSwgJ090aGVycycpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMsICdPdGhlcnMnKSkpDQpkYXRhLmxhdGVzdCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JQ0KICBzdW1tYXJpc2UoY29uZmlybWVkPXN1bShjb25maXJtZWQpLCBuZXcuY29uZmlybWVkPXN1bShuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPXN1bShjdXJyZW50LmNvbmZpcm1lZCksDQogICAgICAgICAgICByZWNvdmVyZWQ9c3VtKHJlY292ZXJlZCksIGRlYXRocz1zdW0oZGVhdGhzKSwgbmV3LmRlYXRocz1zdW0obmV3LmRlYXRocykpICU+JQ0KICBtdXRhdGUoZGVhdGgucmF0ZT0oMTAwICogZGVhdGhzL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKSANCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLCBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQpKSAlPiUNCiAgbXV0YXRlKHJlY292ZXIucmF0ZT0oMTAwICogcmVjb3ZlcmVkL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCmRmX3BvcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBwb3B1bGF0aW9uICIpKQ0KZGZfcG9wIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9wKQ0KZGZfcG9wIDwtIHJlbmFtZShkZl9wb3AsImNvdW50cnkiPSJDb3VudHJ5IikNCmRmX3BvcA0KZGF0YS5sYXRlc3QgPC0gbWVyZ2UoeCA9IGRhdGEubGF0ZXN0LCB5ID0gZGZfcG9wLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSANCmRhdGEubGF0ZXN0IDwtIHJlbmFtZShkYXRhLmxhdGVzdCwicG9wdWxhdGlvbiIgPSAiUG9wdWxhdGlvbiAoMjAyMCkiKQ0KZGF0YS5sYXRlc3QNCmRhdGEubGF0ZXN0ICAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQscmVjb3Zlci5yYXRlLHBvcHVsYXRpb24pKSAlPiUNCiAgbXV0YXRlKGNvbmZpcm0ucmF0ZT0oMTAwICpjb25maXJtZWQvcG9wdWxhdGlvbikgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCg0KYGBgDQpgYGB7cn0NCmRhdGEubGF0ZXN0ICU+JSBtdXRhdGUoZGVhdGgucmF0ZT1kZWF0aC5yYXRlICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJyUnKSkNCg0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KZGF0YS5sYXRlc3QubG9uZyA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSB3aXRoIHByb3BlciB0ZXh0IGFuZCBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sYXRlc3QubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nVG90YWwgRGVhdGhzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoLnJhdGU9J0RlYXRoIFJhdGUgKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQ9J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5kZWF0aHM9J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyLnJhdGUgPSAnUmVjb3ZlciBSYXRlKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm0ucmF0ZSA9ICdDb25maXJtZWQgUmF0ZSglKScpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QubG9uZykNCmRhdGEub25lLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nVG90YWwgQ29uZmlybWVkJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdUb3RhbCBEZWF0aHMnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0N1cnJlbnQgQ29uZmlybWVkJykNCmRhdGEudHdvLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nRGVhdGggUmF0ZSAoJSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdOZXcgRGVhdGhzIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nUmVjb3ZlciBSYXRlKCUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdDb25maXJtZWQgUmF0ZSglKScpDQpkYXRhLnR3by5kZW0NCmBgYA0KDQpgYGB7cn0NCiMjIGJhciBjaGFydA0KZGF0YS5vbmUuZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Miwgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCg0KZGF0YS50d28uZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Miwgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCmBgYA0KDQpgYGB7cn0NCiMjR0RQDQpkZl9nZHAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwIikpDQpkZl9nZHAgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHApDQpkZl9nZHAgPC0gcmVuYW1lKGRmX2dkcCwiY291bnRyeSI9IlJlYWwgR0RQIGdyb3d0aCAoQW5udWFsIHBlcmNlbnQgY2hhbmdlKSIpDQpkZl9nZHAgPC0gc2VsZWN0KGRmX2dkcCxjKCJjb3VudHJ5IiwiMjAxMiIsIjIwMTMiLCIyMDE0IiwiMjAxNSIsIjIwMTYiLCIyMDE3IiwiMjAxOCIsIjIwMTkiLCIyMDIwIiwiMjAyMSIpKQ0KZGZfZ2RwDQpkZl9nZHAyMDE5IDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGdkcDE5IikpDQpkZl9nZHAyMDE5IDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2RwMjAxOSkNCmRmX2dkcDIwMTkNCg0KYGBgDQpgYGB7cn0NCiNoZWFsdGhyYW5raW5nDQpkZl9oZWFsdCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBoZWFsdGhyYW5raW5nIikpDQpkZl9oZWFsdCA8LSBhcy5kYXRhLmZyYW1lKGRmX2hlYWx0KQ0KZGZfaGVhbHQgPC0gc2VsZWN0KGRmX2hlYWx0LGMoImNvdW50cnkiLCJoZWFsdGhDYXJlSW5kZXgiKSkNCmRmX2hlYWx0DQpgYGANCmBgYHtyfQ0KI1RvcDIwUG9ybmh1Yg0KZGZfcG9ybmh1YiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBQb3JuaHViIikpDQpkZl9wb3JuaHViIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9ybmh1YikNCmRmX3Bvcm5odWINCg0KYGBgDQoNCg0KYGBge3J9DQojdGVtcA0KZGZfdGVtcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBBdmdfV29ybGRfVGVtcF8yMDIwIikpDQpkZl90ZW1wIDwtIGFzLmRhdGEuZnJhbWUoZGZfdGVtcCkNCmRmX2NpdHkgPC0gc2VsZWN0KGRmX3RlbXAsYygiQ291bnRyeSIsIkNpdHkiKSkgJT4lDQogIHJlbmFtZShjb3VudHJ5PUNvdW50cnkpICU+JSANCiAgcmVuYW1lKGNpdHk9Q2l0eSkNCm51bW9mY2l0eSA8LSBhZ2dyZWdhdGUoY2l0eSB+IGNvdW50cnksIGRhdGEgPSBkZl9jaXR5LCBsZW5ndGgpDQpkZl90ZW1wIDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJBcHIiLCJNYXkiLCJKdW4iLCJKdWwiLCJBdWciKSkgJT4lDQogIHJlbmFtZShjb3VudHJ5PUNvdW50cnkpDQpkZl90ZW1wIDwtIGRhdGEuZnJhbWUoY291bnRyeT1kZl90ZW1wWywxXSxhdmc9cm93TWVhbnMoZGZfdGVtcFssLTFdKSkNCmRmX3RlbXAgPC0gZGZfdGVtcCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JSBzdW1tYXJpc2UoYXZnX3RlbXAgPSBtZWFuKGF2ZyxuYS5ybSA9IFRSVUUpKQ0KZGZfdGVtcCA8LSBkZl90ZW1wICU+JSBtdXRhdGUoY291bnRyeT1pZmVsc2UoY291bnRyeT09IlVuaXRlZCBTdGF0ZXMiLCJVUyIsIGNvdW50cnkgKSApIA0KZGZfdGVtcCRhdmdfdGVtcCA8LSBkZl90ZW1wJGF2Z190ZW1wICU+JSANCiAgc3ByaW50ZihkZl90ZW1wJGF2Z190ZW1wLCBmbXQgPSAnJSMuMWYnKSAlPiUNCiAgYXMubnVtZXJpYyhkZl90ZW1wJGF2Z190ZW1wKQ0KZGZfdGVtcA0KYGBgDQoNCg0KDQpgYGB7cn0NCiNUb3AgMjAgd2l0aCBnZHANCmRhdGEubG9uZ0dEUCA8LSBkZl9nZHAgJT4lIGdhdGhlcihrZXk9eWVhciwgdmFsdWU9R0RQLCAtYyhjb3VudHJ5KSkNCmRhdGEudG9wIDwtIGRhdGEubGF0ZXN0ICU+JSBmaWx0ZXIoY291bnRyeSE9J1dvcmxkJykNCmRhdGEudG9wIDwtIGhlYWQoZGF0YS50b3AsMjApDQojVmlldyhkYXRhLnRvcCkNCmRhdGEuZ2RwIDwtIGZpbHRlcihkYXRhLmxvbmdHRFAseWVhcj09JzIwMjAnKQ0KI1ZpZXcoZGF0YS5nZHApDQojbWVyZ2UNCm1lcmdjb3VudHJ5ID0gZnVuY3Rpb24oZGF0YTEsZGF0YTIpew0KICBkYXRhIDwtIG1lcmdlKHggPSBkYXRhMSwgeSA9IGRhdGEyLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSANCiAgcmV0dXJuKGRhdGEpDQp9DQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3AsIHkgPSBkZl9nZHAyMDE5LCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUgDQogIHNlbGVjdCgtYyhjb2RlLHJhbmssbmV3LmNvbmZpcm1lZCxuZXcuZGVhdGhzLGN1cnJlbnQuY29uZmlybWVkLHBvcHVsYXRpb24pKSAlPiUgDQogIHJlbmFtZShHRFA9IkdEUCAobWlsbGlvbnMgb2YgVVMgZG9sbGFycykiKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3Aud29ybGQsIHkgPSBkZl9oZWFsdCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lDQogIHJlbmFtZShoZWFsdGhjYXJlPSJoZWFsdGhDYXJlSW5kZXgiKQ0KI2RhdGEudG9wLndvcmxkIDwtIG1lcmdjb3VudHJ5KGRhdGEudG9wLndvcmxkLCBkZl90ZW1wKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3Aud29ybGQsIHkgPSBkZl9wb3JuaHViLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUNCiAgcmVuYW1lKFBvcm5odWIgPSAiUG9ybmh1YkluZGV4KCUpIikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQppbmRleCA8LSBpcy5uYShkYXRhLnRvcC53b3JsZCkNCmRhdGEudG9wLndvcmxkW2luZGV4XSA8LSAwDQpkYXRhLnRvcC53b3JsZA0KI1ZpZXcoZGF0YS50b3Aud29ybGQpDQoNCm5vcm1hbGl6ZSA9IGZ1bmN0aW9uKGRhdGEpew0KICAjcmV0dXJuICgoZGF0YSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpLyhtYXgoZGF0YSxuYS5ybSA9IFRSVUUpIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkpDQogIHogPC0gc2NhbGUoZGF0YSk7DQogIHRhbmgoei8yKQ0KfQ0Kbm9ybV9kYXRhID0gYXMuZGF0YS5mcmFtZShhcHBseShkYXRhLnRvcC53b3JsZFssMjoxMl0sMixub3JtYWxpemUpKQ0KY29ycl9kYXRhIDwtIG5vcm1fZGF0YQ0Kbm9ybV9kYXRhJGNvdW50cnkgPC0gYygiQXJnZW50aW5hIiwiQmFuZ2xhZGVzaCIsIkJyYXppbCIsIkNoaWxlIiwiQ29sb21iaWEiLCJGcmFuY2UiLCJHZXJtYW55IiwiSW5kaWEiLCJJcmFuIiwiSXRhbHkiLCJNZXhpY28iLCJQYWtpc3RhbiIsIlBlcnUiLCJSdXNzaWEiLCJzYXVkaSBBcmFiaWEiLCJTb3V0aCBBZnJpY2EiLCJTcGFpbiIsIlR1cmtleSIsIlVuaXRlZCBLaW5nZG9tIiwiVVMiKQ0KI1ZpZXcobm9ybV9kYXRhKQ0KDQoNCm5vcm1fZGF0YV9wbG90IDwtIHNlbGVjdChub3JtX2RhdGEsImNvdW50cnkiLCJjb25maXJtLnJhdGUiLCJkZWF0aC5yYXRlIiwicmVjb3Zlci5yYXRlIiwiaGVhbHRoY2FyZSIsIlBvcm5odWIiLCJHRFAiLCJhdmdfdGVtcCIpDQpub3JtX2RhdGFfcGxvdCAlPD4lIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnkpKQ0KbGV2ZWxfb3JkZXIgPC0gZmFjdG9yKG5vcm1fZGF0YV9wbG90JHR5cGUsIA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVsID0gYygiR0RQIiwiYXZnX3RlbXAiLCJoZWFsdGhjYXJlIiwicmVjb3Zlci5yYXRlIiwiZGVhdGgucmF0ZSIsImNvbmZpcm0ucmF0ZSIsIlBvcm5odWIiKSkNCmdncGxvdChkYXRhID0gbm9ybV9kYXRhX3Bsb3QsIGFlcyh4PWNvdW50cnksIHk9bGV2ZWxfb3JkZXIsIGZpbGw9Y291bnQpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInBpbmsiLCBoaWdoID0gImJsdWUiKSArDQogIHhsYWIoIiIpICsNCiAgeWxhYigiIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCx2anVzdCA9IDEpKSsNCiAgdGhlbWUoDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICNsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQogIA0KYGBgDQoNCg0KYGBge3J9DQojcmFuayBHRFANCmRhdGEudG9wLmhpZ2h0IDwtIGRhdGEuZ2RwICU+JSBzZWxlY3QoY291bnRyeSwgeWVhcixHRFApICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhHRFApKSkNCmRhdGEudG9wLmhpZ2h0DQprIDwtIDE1DQp0b3AuZ2RwIDwtIGRhdGEudG9wLmhpZ2h0ICU+JSANCiAgI2ZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUgDQogIGFycmFuZ2UocmFua2luZykNCnRvcC5nZHAgPC0gaGVhZCh0b3AuZ2RwLDIxKQ0KZGF0YS50b3AubG93IDwtIGRhdGEuZ2RwICU+JSBzZWxlY3QoY291bnRyeSwgeWVhcixHRFApICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoR0RQKSkNCmxvdy5nZHAubG9uZyA8LSBkYXRhLnRvcC5sb3cgJT4lIA0KICAjZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JSANCiAgYXJyYW5nZShyYW5raW5nKQ0KVmlldyhsb3cuZ2RwLmxvbmcpDQpsb3cuZ2RwIDwtIGhlYWQobG93LmdkcC5sb25nLDIzKQ0KbG93LmdkcA0KYGBgDQoNCg0KDQpgYGB7cn0NCiNjb3JyZWxhdGlvbg0KY29ycl9kYXRhICU8PiUgc2VsZWN0KGMoR0RQLGNvbmZpcm0ucmF0ZSxkZWF0aC5yYXRlLHJlY292ZXIucmF0ZSxoZWFsdGhjYXJlLGF2Z190ZW1wLFBvcm5odWIpKQ0KaGVhZChjb3JyX2RhdGEpDQpjb3IoY29ycl9kYXRhKQ0KZ2djb3JycGxvdChjb3IoY29ycl9kYXRhKSxoYy5vcmRlciA9IFRSVUUsDQogICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICBjb2xvcnMgPSBjKCIjNkQ5RUMxIiwid2hpdGUiLCIjRTQ2NzI2IiksDQogICAgICAgICAgIGxhYiA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KZGYgPC0gZGF0YS5sb25nICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMpICU8PiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMpKSkNCmRmICU+JSBmaWx0ZXIoY291bnRyeSAhPSAnV29ybGQnICYgdHlwZSAhPSAnVG90YWwgQ29uZmlybWVkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50LCBmaWxsPXR5cGUpKSArDQogIGdlb21fYXJlYShhbHBoYT0wLjUpICsNCiMgeGxhYignJykgKyB5bGFiKCcnKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENPVklELTE5IENhc2VzIGluIFRvcCAyMCBDb3VudHJpZXMgLSAnLA0KICAgICAgICAgICAgICAgICAgICBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuNCwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgc3RyaXAudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH5jb3VudHJ5LCBuY29sPTQsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCnAgPC0gZGYgJT4lIGZpbHRlcihjb3VudHJ5ICE9ICdXb3JsZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCwgY29sb3I9dHlwZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDT1ZJRC0xOSBDYXNlcyBpbiBUb3AgMjAgQ291bnRyaWVzIChsb2cgc2NhbGUpIC0gJywNCiAgICAgICAgICAgICAgICAgICAgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3B1cnBsZScsICdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuNCwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgc3RyaXAudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCnAgKyBmYWNldF93cmFwKH5jb3VudHJ5LCBuY29sPTQsIHNjYWxlcz0nZnJlZV95JykNCmBgYA0KYGBge3J9DQpkYXRhLndvcmxkICU8PiUgYXJyYW5nZShkZXNjKGRhdGUpKSAlPiUNCiAgc2VsZWN0KGMoZGF0ZSwgY29uZmlybWVkLCBkZWF0aHMsIHJlY292ZXJlZCwgY3VycmVudC5jb25maXJtZWQsbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywgbmV3LnJlY292ZXJlZCwgcmF0ZS5sb3dlciwgcmF0ZS51cHBlciwgcmF0ZS5kYWlseSkpDQpkYXRhLndvcmxkICU+JQ0KICBtdXRhdGUocmF0ZS51cHBlciA9IHJhdGUudXBwZXIgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnXFwlJyksDQogICAgICAgICByYXRlLmxvd2VyID0gcmF0ZS5sb3dlciAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCdcXCUnKSwNCiAgICAgICAgIHJhdGUuZGFpbHkgPSByYXRlLmRhaWx5ICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJ1xcJScpKSANCmBgYA0KYGBge3J9DQojc2Fyc18yMDAzDQpkZl9zYXJzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHNhcnNfMjAwM191cGRhdGUiKSkNCmRmX3NhcnMgPC0gYXMuZGF0YS5mcmFtZShkZl9zYXJzKQ0KZGZfc2Fycw0KYGBgDQoNCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIGRhdGUNCiNkYXRlc1NhciA8LSBhcy5EYXRlKGRmX3NhcnMkRGF0ZSxmb3JtYXQgPSAiJW0vJWQvJXkiKQ0KDQpkZl9zYXJzICU8PiUgIG11dGF0ZShEYXRlID0gYXMuRGF0ZShkZl9zYXJzJERhdGUsZm9ybWF0ID0gIiVtLyVkLyV5IikpDQpkZl9zYXJzDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KZGF0YVNhci5sb25nIDwtIGRmX3NhcnMgJT4lDQogIHNlbGVjdChjKERhdGUsIGNvdW50cnksIEN1bXVsYXRpdmVfbnVtYmVyICwgTnVtYmVyX2RlYXRocywgTnVtYmVyX3JlY292ZXJlZCkpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBEYXRlKSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YVNhci5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBDdW11bGF0aXZlX251bWJlciA9J0N1bXVsYXRpdmUgTnVtYmVyJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyX2RlYXRocyA9J051bWJlciBvZiBkZWF0aHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXJfcmVjb3ZlcmVkID0nTnVtYmVyIG9mIHJlY292ZXJlZCcpKQ0KVmlldyhkYXRhU2FyLmxvbmcpDQogDQpgYGANCg0KDQoNCg0KYGBge3J9DQpnIDwtDQogIGdncGxvdChkYXRhU2FyLmxvbmcsYWVzKERhdGUsY291bnQsY29sb3IgPSB0eXBlKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgZ2VvbV9wb2ludCgpKw0KICB4bGFiKCIiKSsNCiAgeWxhYigiIikNCmcNCiAgDQpgYGANCmBgYHtyfQ0KI0NvdmlkX1RoYWlsYW5kDQpkZl90aGFpIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkX1RoYWlsYW5kIikpDQpkZl90aGFpIDwtIGFzLmRhdGEuZnJhbWUoZGZfdGhhaSkNCmRmX3RoYWkNCmBgYA0KDQpgYGB7cn0NCiNjbGVhbiBDb3ZpZF9UaGFpbGFuZA0KZGF0ZXMudGggPC0gZGZfdGhhaVssMl0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzLnRoKQ0KbWluLmRhdGUudGggPC0gbWluKGRhdGVzLnRoKQ0KbWF4LmRhdGUudGggPC0gbWF4KGRhdGVzLnRoKQ0KbWluLmRhdGUudHh0LnRoIDwtIG1pbi5kYXRlLnRoICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dC50aCA8LSBtYXguZGF0ZS50aCAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCmBgYHtyfQ0KZGZfdGhhaSRhbm5vdW5jZV9kYXRlIDwtIG1keShkZl90aGFpJGFubm91bmNlX2RhdGUpDQpkZl90aGFpJG5vdGlmaWNhdGlvbl9kYXRlIDwtIG1keShkZl90aGFpJG5vdGlmaWNhdGlvbl9kYXRlKQ0KZGZfdGhhaSANCmBgYA0KYGBge3J9DQpkZl90aGFpIDwtIGRmX3RoYWkgJT4lIHNlbGVjdCghTm8uKSAlPiUgc2VsZWN0KCFub3RpZmljYXRpb25fZGF0ZSkgJT4lIA0KICBncm91cF9ieShhbm5vdW5jZV9kYXRlKQ0KZGZfdGhhaQ0KYGBgDQpgYGB7cn0NCiMgVG90YWwgY29uZmlybWVkIGNhc2VzIGluIFRoYWlsYW5kDQpkYXRhLnRoYWkuY291bnQgPC0gZGZfdGhhaSAlPiUNCiAgc2VsZWN0KGFubm91bmNlX2RhdGUpICU+JQ0KICBzdW1tYXJpc2UoY29tZmlybWVkID0gbigpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KZGF0YS50aGFpLmNvdW50JGN1bXVsYXRpdmVfY29uZmlybWVkIDwtIGN1bXN1bShkYXRhLnRoYWkuY291bnRbLCAyXSkNCmRhdGEudGhhaS5jb3VudA0KYGBgDQoNCg0KYGBge3J9DQojIyBUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMQ0KcGxvdDEgPC0gZ2dwbG90KGRhdGEudGhhaS5jb3VudCwgYWVzKHg9YW5ub3VuY2VfZGF0ZSwgeT1jdW11bGF0aXZlX2NvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoIiAiKSArIHlsYWIoIkNvdW50IikgKyBsYWJzKHRpdGxlPSdUaGFpIEN1bXVsYXRpdmUgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKScpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y29tZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYigiICIpICsgeWxhYigiQ291bnQiKSsgbGFicyh0aXRsZT0nVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEpJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyB0d28gcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTEpDQpgYGANCmBgYHtyfQ0KIyMgVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEpIGxvZyBzY2FsZQ0KcGxvdDEgPC0gZ2dwbG90KGRhdGEudGhhaS5jb3VudCwgYWVzKHg9YW5ub3VuY2VfZGF0ZSwgeT1jdW11bGF0aXZlX2NvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoIiAiKSArIHlsYWIoIkNvdW50IikgKyBsYWJzKHRpdGxlPSdUaGFpIEN1bXVsYXRpdmUgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxIGxvZyBzY2FsZSknKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpK3NjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEudGhhaS5jb3VudCwgYWVzKHg9YW5ub3VuY2VfZGF0ZSwgeT1jb21maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCIgIikgKyB5bGFiKCJDb3VudCIpKyBsYWJzKHRpdGxlPSdUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSBsb2cgc2NhbGUpJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKStzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0xKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSBzZXggKGdlbmRlcikNCmRhdGEudGhhaS5nZW5kZXIgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkoc2V4KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQgPSAoY291bnQgLyBzdW0oY291bnQpICogMTAwKSAlPiUgcm91bmQoMikpICU+JQ0KICBmaWx0ZXIocGVyY2VudD4xKSU+JQ0KICAjbXV0YXRlKHBvcyA9IGN1bXN1bShwZXJjZW50KSAtIDAuNSpwZXJjZW50KSAlPiUNCiAgYXJyYW5nZShkZXNjKHBlcmNlbnQpKQ0KZGF0YS50aGFpLmdlbmRlcg0KYGBgDQpgYGB7cn0NCmRhdGEudGhhaS5nZW5kZXIkc2V4IDwtIGZhY3RvcihkYXRhLnRoYWkuZ2VuZGVyJHNleCwgbGV2ZWxzID0gYXMuY2hhcmFjdGVyKGRhdGEudGhhaS5nZW5kZXIkc2V4KSkNCmRhdGEudGhhaS5nZW5kZXIkc2V4DQpnLnRoLmdlbmRlciA8LSBkYXRhLnRoYWkuZ2VuZGVyICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50LCBmaWxsID0gc2V4KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArDQogIGNvb3JkX3BvbGFyKCJ5IikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBsYWJzKHRpdGxlPSdHZW5kZXIgb2YgVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEpJykrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocGVyY2VudCwgIiUiKSksIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgDQpnLnRoLmdlbmRlcg0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSByaXNrDQpkYXRhLnRoYWkucmlzayA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShyaXNrKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCmRhdGEudGhhaS5yaXNrDQpgYGANCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSByaXNrDQpkYXRhLnRoYWkucmlzayA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShyaXNrKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQgPSAoY291bnQgLyBzdW0oY291bnQpICogMTAwKSAlPiUgcm91bmQoMikpICU+JQ0KICBmaWx0ZXIocGVyY2VudD4zLjUpICU+JQ0KICBhcnJhbmdlKGRlc2MocGVyY2VudCkpIA0KZGF0YS50aGFpLnJpc2sNCmBgYA0KYGBge3J9DQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkMiXSA8LSAiQ2xvc2UgY29udGFjdCB3aXRoIHRoZSBwYXRpZW50Ig0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJGIl0gPC0gIlN0YXRlIFF1YXJhbnRpbmUiDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIk8iXSA8LSAiQU9RL0FMUS9IUS9BSFEvT1EiDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkciXSA8LSAiR28gdG8gYSBjcm93ZGVkIHBsYWNlIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJCIl0gPC0gIlRoYWkgcGVvcGxlIGZyb20gYWJyb2FkIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJEIl0gPC0gIkNhcmVlciBhdCByaXNrIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJIIl0gPC0gIkNhYmFyZXQiDQpkYXRhLnRoYWkucmlzaw0KYGBgDQoNCg0KYGBge3J9DQpkYXRhLnRoYWkucmlzayRyaXNrIDwtIGZhY3RvcihkYXRhLnRoYWkucmlzayRyaXNrLCBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZGF0YS50aGFpLnJpc2skcmlzaykpDQpkYXRhLnRoYWkucmlzayRyaXNrDQpnLnRoLnJpc2sgPC0gZGF0YS50aGFpLnJpc2sgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IHBlcmNlbnQsIGZpbGwgPSByaXNrKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIGxhYnModGl0bGU9J1Jpc2sgb2YgVGhhaSBDb25maXJtZWQgQ2FzZXMoSmFuIDIwMjAgLSBKYW4gMjAyMSknKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAiQmxhY2siLCBzaXplID0gMywgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSANCmcudGgucmlzaw0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgYWdlDQpkYXRhLnRoYWkuYWdlIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KGFnZSxzZXgpICU+JSANCiAgZmlsdGVyKHNleCAhPSAiIiklPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCmRhdGEudGhhaS5hZ2UNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoZGF0YS50aGFpLmFnZSxhZXMoeD1hZ2UseT1jb3VudCxmaWxsPXNleCkpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgbGFicyh0aXRsZT0nQWdlIG9mIFRoYWkgQ29uZmlybWVkIENhc2VzJykrZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUKSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSBuYXRpb25hbGl0eQ0KZGF0YS50aGFpLm5hdGlvbmFsaXR5IDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KG5hdGlvbmFsaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgZmlsdGVyKGNvdW50ID4gMTMpJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQojZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5W25hdGlvbmFsaXR5JG5hdGlvbmFsaXR5ID09ICI/Pz8/Pz8/PyJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5DQpnZ3Bsb3QoZGF0YS50aGFpLm5hdGlvbmFsaXR5LGFlcyh4PW5hdGlvbmFsaXR5LHk9Y291bnQpKStnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrDQogIGxhYnModGl0bGU9J05hdGlvbmFsaXR5IG9mIFRoYWkgQ29uZmlybWVkIENhc2VzJykrY29vcmRfZmxpcCgpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0K